package com.weilele.mvvm.base

import android.content.Context
import android.content.ContextWrapper
import android.content.DialogInterface
import android.graphics.Point
import android.graphics.Rect
import android.graphics.RectF
import android.os.Bundle
import android.util.AttributeSet
import android.view.*
import android.widget.FrameLayout
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import com.weilele.mvvm.R
import com.weilele.mvvm.base.helper.*
import com.weilele.mvvm.base.helper.annotation.dealViewModelAnnotation
import com.weilele.mvvm.utils.activity.getScreenInfo
import com.weilele.mvvm.utils.activity.hiddenKeyboard
import com.weilele.mvvm.utils.fragment.setScreenAdaptation
import com.weilele.mvvm.utils.result_contract.NavigateForResultHelper
import com.weilele.mvvm.utils.result_contract.PermissionForResultHelper
import com.weilele.mvvm.utils.safeGet
import com.weilele.mvvm.utils.tryError
import com.weilele.mvvm.widget.BaseFrameLayout
import java.lang.ref.WeakReference

/**
 * 描述：mvvm--基类对话框
 */
abstract class MvvmDialog : AppCompatDialogFragment(), BaseMvvmInterface {
    val navigateForResultHelper by lazy { NavigateForResultHelper(this) }
    val permissionForResultHelper by lazy { PermissionForResultHelper(this) }
    private var isCreateLiveData = false
    private var dialogView: View? = null
    private val showOrHideLiveData: StatusLiveData<Pair<FragmentManager?, Boolean?>> by lazy {
        //不能使用自己的生命周期，因为没有调用show，自己的生命周期是收不到回调的，只能使用宿主的生命周期
        //所以在显示对话框之前，必须先对  inContextWrf 赋值
        val activity = appCompatActivity
                ?: inContextWrf?.get()?.safeGet<AppCompatActivity>()
                ?: throw IllegalArgumentException("调用safeShow 必须需要预先设置一个AppCompatActivity")
        val data = createStatusLiveData<Pair<FragmentManager?, Boolean?>>()
        isCreateLiveData = true
        data.observe(activity, safeShowObserver)
        data
    }
    private var isDialogCreate = false
    private val safeShowObserver = Observer<StatusValue<Pair<FragmentManager?, Boolean?>>> {
        it.value?.second?.let { show ->
            if (show) {
                if (!isDialogCreate) {
                    tryError {
                        it.value?.first?.let { fmm ->
                            show(fmm, javaClass.simpleName)
                        }
                    }
                }
            } else {
                if (isDialogCreate) {
                    dismiss()
                }
            }
        }
    }

    /**
     * 通过show方法传入的context
     */
    private var inContextWrf: WeakReference<Context?>? = null

    private var atLocationBean: AtLocationBean? = null

    /**
     * 如果atLocationBean不是null，就代表本次弹窗类似于popupWindow
     */
    private data class AtLocationBean(
            val anchor: View,
            val isGravityEnd: Boolean,
            val xoff: Int = 0,
            val yoff: Int = 0
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        //跳转帮助，必须在onCreate注册
        navigateForResultHelper
        permissionForResultHelper
        //由于activity显示键盘的时候弹出对话框，会影响window的尺寸，这里就先隐藏键盘
        appCompatActivity?.hiddenKeyboard()
        isDialogCreate = true
        super.onCreate(savedInstanceState)
        dealViewModelAnnotation()
    }

    /**
     * 获取view
     */
    override fun getLayoutResOrView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): Any? {
        return getLayoutResOrView(inflater, container)
    }

    /**
     * 创建view
     */
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        setScreenAdaptation()
        val contentView = getLayoutResOrView(inflater, container, savedInstanceState)
        val rootView = FrameLayout(inflater.context)
        //用于响应触摸外部消失
        val rootTouchView = TouchDismissView(inflater.context)
        rootView.addView(
                rootTouchView, FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.MATCH_PARENT
        )
        )
        val contentViewGroup = when (contentView) {
            is Int -> {
                inflater.inflate(contentView, rootView, false)
            }
            is View -> {
                contentView
            }
            else -> {
                null
            }
        }
        dialogView = contentViewGroup
        //子类对话框根布局默认参数
        val lp = contentViewGroup?.layoutParams.safeGet<FrameLayout.LayoutParams>()
                ?: FrameLayout.LayoutParams(
                        FrameLayout.LayoutParams.MATCH_PARENT,
                        FrameLayout.LayoutParams.WRAP_CONTENT
                )
        onRootViewLayoutParams(lp)
        contentViewGroup?.let {
            rootView.addView(contentViewGroup, lp)
        }
        setDialogWindowAnim(lp)
        return rootView
    }

    /**
     * 保存所有的注册监听者
     */
    private val observerLiveDatas by lazy { mutableListOf<LiveDataWrap>() }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        getObserverLiveData()?.also {
            it.observer(this)
            observerLiveDatas.addAll(it)
        }
        getObserverLiveDataForever()?.also {
            it.observeForever()
            observerLiveDatas.addAll(it)
        }
        getClickView()?.forEach {
            it?.setOnClickListener(this)
        }
        //这里支持覆盖设置的点击事件
        initUi(savedInstanceState)
        initData()
        updateWindow()
    }

    override fun onDestroy() {
        inContextWrf?.clear()
        inContextWrf = null
        if (isCreateLiveData) {
            showOrHideLiveData.removeObserver(safeShowObserver)
        }
        isDialogCreate = false
        super.onDestroy()
    }

    /**
     * 开始进入动画
     */
    private fun setDialogWindowAnim(lp: FrameLayout.LayoutParams) {
        val locationBean = atLocationBean
        //如果是通过调用showAsDropTopOrBottom显示的对话框，取消动画
        //因为这里的动画是整个对话框，但是整个对话框是全屏的，
        // 这里我们可能只需要自己布局的动画，如果有需要自行处理
        getShowViewLocation(lp, locationBean)
        if (locationBean != null) {
            return
        }
        if (!isNeedDialogAnim()) {
            return
        }
        val anim = getDialogAnim(lp)
        if (anim != null) {
            dialog?.window?.setWindowAnimations(anim)
        }
    }


    /**
     * 设置view显示位置
     */
    private fun getShowViewLocation(lp: FrameLayout.LayoutParams, locationBean: AtLocationBean?) {
        locationBean ?: return
        val activity = appCompatActivity ?: return
        val anchor = locationBean.anchor
        val xoff = locationBean.xoff
        val yoff = locationBean.yoff
        val touch = intArrayOf(0, 0)
        val screenPoint = Point()
        locationBean.anchor.display.getRealSize(screenPoint)
        locationBean.anchor.getLocationOnScreen(touch)
        val isShowBottom = touch[1] < screenPoint.y / 2
        val screenInfo = activity.getScreenInfo()
        return if (isShowBottom) {
            //如果有状态栏，需要减去状态栏高度
            //经过未完全测试，不论怎样都有状态栏高度
            val topMargin = (touch[1] + anchor.height + yoff) - screenInfo.screenHeight
            if (locationBean.isGravityEnd) {
                lp.gravity = Gravity.TOP or Gravity.END
                lp.topMargin = topMargin
                lp.marginEnd = screenPoint.x - touch[0] - anchor.width + xoff

            } else {
                lp.gravity = Gravity.TOP or Gravity.START
                lp.topMargin = topMargin
                lp.marginStart = touch[0] + xoff
            }
        } else {
            //如果有导航栏，需要减去导航栏高度
            val bottomMargin = (screenPoint.y - touch[1] + yoff) - screenInfo.navigationBarHeight
            if (locationBean.isGravityEnd) {
                lp.gravity = Gravity.BOTTOM or Gravity.END
                lp.bottomMargin = bottomMargin
                lp.marginEnd = screenPoint.x - touch[0] - anchor.width + xoff
            } else {
                lp.gravity = Gravity.BOTTOM or Gravity.START
                lp.bottomMargin = bottomMargin
                lp.marginStart = touch[0] + xoff
            }
        }
    }

    /**
     * 当生命周期可见的时候再取消对话框
     */
    fun safeDismiss() {
        showOrHideLiveData.success(Pair(null, false))
    }

    override fun dismiss() {
        if (dialog?.isShowing != true) {
            return
        }
        try {
            super.dismiss()
        } catch (e: Exception) {
            dismissAllowingStateLoss()
        }
    }


    override fun onDestroyView() {
        observerLiveDatas.removeObserver()
        super.onDestroyView()
    }

    private fun updateWindow() {
        dialog?.setCanceledOnTouchOutside(isCanceledOnTouchOutside())
        updateWindowAttributes()
    }

    private fun updateWindowAttributes() {
        dialog?.window?.apply {
            attributes?.let {
                //保证对话框弹出的时候状态栏不是黑色
                val outRect = Rect()
                appCompatActivity?.window?.decorView?.getWindowVisibleDisplayFrame(outRect)
                val height = outRect.bottom - outRect.top
                it.height = if (height > 0) height else WindowManager.LayoutParams.MATCH_PARENT
                val width = outRect.width()
                it.width = if (width > 0) width else WindowManager.LayoutParams.MATCH_PARENT
                it.dimAmount = getDialogDimAmount()
                dialog?.window?.attributes = it
            }
            setBackgroundDrawableResource(android.R.color.transparent)
        }
    }

    /**
     * 设置对话框透明度
     */
    fun setDialogDimAmount(dimAmount: Float) {
        dialog?.window?.attributes?.apply {
            this.dimAmount = dimAmount
            dialog?.window?.attributes = this
        }
    }


    /**
     * 显示在按钮的上方或者下方
     *  isGravityEnd : true ,弹窗与view的右边缘对齐，可以通过设置xoff来横向偏移
     *  isGravityEnd : false ,弹窗与view的左边缘对齐，可以通过设置xoff来横向偏移
     */
    fun showAsDropTopOrBottom(anchor: View, isGravityEnd: Boolean, xoff: Int = 0, yoff: Int = 0) {
        atLocationBean = AtLocationBean(anchor, isGravityEnd, xoff, yoff)
        when (val ctx = anchor.context) {
            is AppCompatActivity -> {
                show(ctx)
            }
            is ContextWrapper -> {
                show(ctx.baseContext.safeGet<AppCompatActivity>())
            }
        }
    }

    /**
     * 显示在按钮的上方或者下方
     *  isGravityEnd : true ,弹窗与view的右边缘对齐，可以通过设置xoff来横向偏移
     *  isGravityEnd : false ,弹窗与view的左边缘对齐，可以通过设置xoff来横向偏移
     */
    fun safeShowAsDropTopOrBottom(
            anchor: View,
            isGravityEnd: Boolean,
            xoff: Int = 0,
            yoff: Int = 0
    ) {
        atLocationBean = AtLocationBean(anchor, isGravityEnd, xoff, yoff)
        when (val ctx = anchor.context) {
            is AppCompatActivity -> {
                safeShow(ctx)
            }
            is ContextWrapper -> {
                safeShow(ctx.baseContext.safeGet<AppCompatActivity>())
            }
        }
    }

    fun show(activity: FragmentActivity?) {
        setContextWrf(activity)
        activity?.let {
            tryError {
                show(it.supportFragmentManager, javaClass.simpleName)
            }
        }
    }

    fun show(fragment: Fragment?) {
        setContextWrf(fragment?.context)
        fragment?.childFragmentManager?.let {
            tryError {
                show(it, javaClass.simpleName)
            }
        }
    }

    open fun setOnShowListener(@androidx.annotation.Nullable listene: DialogInterface.OnShowListener) {
        dialog?.setOnShowListener(listene)
    }

    open fun setOnDismissListener(@androidx.annotation.Nullable listene: DialogInterface.OnDismissListener) {
        dialog?.setOnDismissListener(listene)
    }

    /**
     * 当生命周期可见再显示对话框
     */
    fun safeShow(fragmentActivity: FragmentActivity?) {
        setContextWrf(fragmentActivity)
        val fgm = fragmentActivity?.supportFragmentManager ?: return
        showOrHideLiveData.success(Pair(fgm, true))
    }

    /**
     * 当生命周期可见再显示对话框
     */
    fun safeShow(fragment: Fragment?) {
        setContextWrf(fragment?.context)
        val fgm = fragment?.childFragmentManager ?: return
        showOrHideLiveData.success(Pair(fgm, true))
    }

    private fun setContextWrf(context: Context?) {
        context ?: return
        if (inContextWrf?.get() == null) {
            inContextWrf = WeakReference(context)
        }
    }

    /**
     * 设置对话框背景透明度
     * 0-1
     */
    protected open fun getDialogDimAmount(): Float {
        return 0.6f
    }

    /**
     * 触摸外面是否可以取消
     */
    protected open fun isCanceledOnTouchOutside() = isCancelable

    /**
     * 设置布局参数
     */
    protected open fun onRootViewLayoutParams(lp: FrameLayout.LayoutParams) {
        lp.gravity = Gravity.CENTER
    }

    /**
     * 设置对话框进入退出动画
     */
    @StyleRes
    protected open fun getDialogAnim(lp: FrameLayout.LayoutParams): Int? {
        return when (lp.gravity) {
            Gravity.BOTTOM -> {
                R.style.PopupWindowBottomIn
            }
            Gravity.TOP -> {
                R.style.PopupWindowTopIn
            }
            else -> {
                R.style.AnimScaleCenter
            }
        }
    }

    /**
     * 是否需要默认,进入退出动画
     */
    protected open fun isNeedDialogAnim(): Boolean = true

    private inner class TouchDismissView : BaseFrameLayout {
        constructor(context: Context) : super(context)
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
                context,
                attrs,
                defStyleAttr
        )

        private val bounds = RectF()
        override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
            if (ev?.action == MotionEvent.ACTION_DOWN) {
                val view = dialogView
                if (view == null) {
                    if (isCancelable && isCanceledOnTouchOutside()) {
                        dismiss()
                    }
                } else {
                    bounds.set(
                            view.left.toFloat(),
                            view.top.toFloat(),
                            view.right.toFloat(),
                            view.bottom.toFloat()
                    )
                    if (!bounds.contains(ev.x, ev.y)) {
                        if (isCancelable && isCanceledOnTouchOutside()) {
                            dismiss()
                        }
                    }
                }
            }
            return super.onInterceptTouchEvent(ev)
        }
    }
}
